/**
 * 组合模式：将对象组合成树形结构以表示“部分-整体”的层次结构，组合模式使得客户端对单个对象和组合对象的使用具有一直性。
 * 可以将组合模式想象成一根大树，将大树分成树枝和树叶两部分，树枝上可以再长树枝，也可以长树叶，树叶上则不能再长出别的东西。
 * 组合模式适合应用场景:
 * 1. 需要实现树状对象结构，可以使用组合模式。
 * 组合模式为你提供了两种共享公共接口的基本元素类型： 简单叶节点和复杂容器。 容器中可以包含叶节点和其他容器。 这使得你可以构建树状嵌套递归对象结构。
 * 2. 希望客户端代码以相同方式处理简单和复杂元素， 可以使用该模式。
 * 组合模式中定义的所有元素共用同一个接口。 在这一接口的帮助下， 客户端不必在意其所使用的对象的具体类。
 */
#pragma once

#include <iostream>
#include <list>
#include <memory>
#include <string>

using namespace std;

namespace composite
{
// Component抽象基类，为组合中的对象声明接口,声明了类共有接口的缺省行为(如这里的add,remove函数),
// 声明一个接口函数可以访问Component的子组件.
class Component
{
public:
    //纯虚函数，只提供接口，没有默认的实现
    virtual void operation() = 0;

    // 虚函数,提供接口,有默认的实现就是什么都不做
    virtual void add(shared_ptr<Component>) {}
    virtual void remove(shared_ptr<Component>) {}
    virtual ~Component() {}
protected:
    Component(const string& name) : m_name(name) {}
    string m_name;
};

//Leaf是叶子结点,也就是不含有子组件的结点类，所以不用实现add、remove等方法
class Leaf : public Component
{
public:
    Leaf(const string& name) : Component(name) {}
    ~Leaf() {cout << m_name << ":" << "~Leaf()" << endl;}
    //只实现Operation接口
    virtual void operation() {
        cout << m_name << ":" << "operation()" << endl;
    }
};

//Composite：含有子组件的类
class Composite : public Component
{
public:
    Composite(const string& name) : Component(name) {}
    ~Composite() {cout << m_name << ":" << "~Composite()" << endl;}
    //实现所有接口
    void operation() {
        cout << m_name << ":" << "operation()" << endl;
        list<shared_ptr<Component>>::iterator iter = m_list.begin();
        for (; iter != m_list.end(); iter++) {
            iter->get()->operation();
        }
    }
    void add(shared_ptr<Component> com) {m_list.push_back(com);}
    void remove(shared_ptr<Component> com) {m_list.remove(com);}
private:
    list<shared_ptr<Component>> m_list;
};

void ClientCode()
{
    /*
      不管是叶子Leaf还是Composite对象pRoot、pCom都实现了operation接口，所以可以一致对待，直接调用operation()
      体现了“使得用户对单个对象和组合对象的使用具有一致性。”
    */
    unique_ptr<Composite> pRoot(new Composite("Root"));

    //组合对象添加叶子节点
    pRoot->add(shared_ptr<Leaf>(new Leaf("Leaf")));

    shared_ptr<Leaf> pLeaf1(new Leaf("Leaf1"));
    shared_ptr<Leaf> pLeaf2(new Leaf("Leaf2"));

    //这里的叶子再添加叶子是没有意义的。
    //由于叶子与组合对象继承了相同的接口，所以语法上是对的，实际上什么也没做(继承自基类Component的add方法)。
    //叶子节点只实现了operation方法，其他add、remove都继承自基类，没有实际意义。
    pLeaf1->add(pLeaf2);
    pLeaf1->remove(pLeaf2);
    //执行叶子operation操作
    pLeaf1->operation();

    //组合对象实现了基类Component的所有接口，所以可以做各种操作(add、remove、operation)。
    shared_ptr<Composite> pCom(new Composite("Com"));
    //组合对象添加叶子节点
    pCom->add(pLeaf1);
    //组合对象添加叶子节点
    pCom->add(pLeaf2);
    //执行组合对象operation操作
    pCom->operation();

    //组合对象添加组合对象
    pRoot->add(pCom);

    pCom->remove(pLeaf1);

    //执行组合对象operation操作
    pRoot->operation();
}
} //namespace composite